﻿using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;

namespace Hsenl {
    [Serializable]
    public class DBRepository {
        private const string CollectionExtension = ".hdbcol"; // hsenl db collection

        [JsonProperty]
        private long _createId = IdHelper.GenerateSnowflakeId();

        private Dictionary<string, DBCollection> _collections = new(); // key: path

        [JsonIgnore]
        public long CreateId => this._createId;

        [JsonIgnore]
        public string RootDir { get; private set; }

        [JsonIgnore]
        public string FileName { get; internal set; } // 临时储存这次加载时的文件名

        [JsonIgnore]
        public string FilePath { get; internal set; } // 临时储存这次加载时的文件路径

        internal void SetRoot(string dir) {
            if (!Directory.Exists(dir)) {
                Directory.CreateDirectory(dir);
            }

            this.RootDir = dir;
        }

        private string GetPath(string fileName) {
            if (string.IsNullOrEmpty(this.RootDir)) {
                throw new Exception("Net set Collections Root Dir");
            }

            var path = Path.Combine(this.RootDir, fileName + CollectionExtension);
            return path;
        }

        // 加载跟目录下所有的仓库(会清空之前加载的)
        public void LoadAllCollections() {
            if (string.IsNullOrEmpty(this.RootDir)) {
                throw new Exception("Collections Root Dir is null");
            }

            this.ClearCache();

            var filePaths = IOHelper.GetAllFiles(this.RootDir, includeSub: false, searchPattern: $"*{CollectionExtension}");
            foreach (var filePath in filePaths) {
                this.LoadCollection(filePath);
            }
        }

        // 加载一个仓库到缓存中
        private DBCollection LoadCollection(string path) {
            if (this._collections.TryGetValue(path, out var col)) {
                Log.Error($"Collection '{path}' is already loaded");
                return null;
            }

            using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            fs.Seek(0, SeekOrigin.Begin);
            var bytes = new byte[fs.Length];
            fs.Read(bytes, 0, bytes.Length);

            col = DatabaseManager.Instance.Serialize.Deserialize<DBCollection>(bytes);
            if (col != null) {
                this._collections.Add(path, col);
                col.FileName = Path.GetFileNameWithoutExtension(path);
                col.FilePath = path;
                Log.Info($"Load collection succ. '{path}'");
                return col;
            }

            return null;
        }

        // 根目录中是否存在某个仓库
        private bool ExistCollection(string path) {
            return File.Exists(path);
        }

        // 有则直接用, 没有则加载
        public DBCollection UseCollection(string name) {
            var path = this.GetPath(name);

            // 尝试获取
            var col = this.GetCollection(name);

            // 尝试加载
            if (col == null) {
                // 先尝试加载
                col = this.LoadCollection(path);
            }

            return col;
        }

        public DBCollection CreateCollection(string name) {
            if (!PathHelper.IsValidFileNameOrFolderName(name)) {
                Log.Error($"Invalid Collection Name:'{name}'");
                return null;
            }
            
            var path = this.GetPath(name);
            if (this.ExistCollection(path)) {
                Log.Error($"Collection '{path}' is already in exist");
                return null;
            }

            var col = new DBCollection();
            var bin = DatabaseManager.Instance.Serialize.Serialize(col);
            using var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
            fs.SetLength(0);
            fs.Write(bin);

            col.FileName = name;
            col.FilePath = path;
            this._collections.Add(path, col);

            Log.Info($"New collection succ. '{path}'");
            return col;
        }

        // 从缓存中获取仓库
        public DBCollection GetCollection(string name) {
            var path = this.GetPath(name);
            return this._collections.TryGetValue(path, out var col) ? col : null;
        }

        private void ClearCache() {
            this._collections.Clear();
        }

        public bool DeleteCollection(string name) {
            var path = this.GetPath(name);
            this._collections.Remove(path, out var deleter);

            if (File.Exists(path)) {
                File.Delete(path);
                return true;
            }

            return false;
        }
    }
}